home *** CD-ROM | disk | FTP | other *** search
- Path: news.itl.net!newsmaster@olympus.itl.net
- From: pauls@dyna.mhs.compuserve.com (Paul Seymour)
- Newsgroups: comp.lang.c
- Subject: Recovery from Division By Zero
- Date: Wed, 07 Feb 1996 05:46:03 GMT
- Organization: Supernet
- Message-ID: <4f8hvg$lbj@fhbgb1.itl.net>
- Reply-To: pauls@dyna.mhs.compuserve.com
- NNTP-Posting-Host: dialup058.itl.net
- Keywords: Signal, Divizion By Zero, ANSI, MSDOS, WATCOM
- X-Newsreader: Forte Free Agent 1.0.82
-
- Reply To: pauls@dyna.mhs.compuserve.com
-
- I am porting a MSDOS application from 16Bit MS Visual C++ 1.5 to,
- a 32Bit DOS4GW Watcom C++ v10.0a application. The application has
- grown too large for conventional memory. The port has been successful
- including,an interrupt handler for (IRQ5, INT0xD) which resets the
- program by, longjmp() 'ing back to where the program was first
- initialised.
- The problem is implementing a handler for division by 0, and other
- maths exceptions, that are not handled by matherr. The program needs
- to handle the DIV0 exception, and reset back to a know good state. The
- way this was successfully done in the 16bit MSVC version was to setup
- an interrupt handler for INT0. Which then puts an error message on the
- display and longjmp() 's back to the same point as the INT0xD handler
- does.
- This method does no work with Watcom C, so I thought I'd defined a
- SIG_FPE handler instead, using the ANSI C signal function. This does
- not work either, under Watcom C. So I wrote two complete test programs
- to test with different,compilers. I still have to use Watcom C to
- build my program (3rd party libraries etc.), but I wanted to confirm
- that my test programs would work.
- The program that uses the interrupt method has direct text output
- routines so that interrupt handlers can display output. Using
- _dos_setvect() to register an interrupt vector worked for the INT0xD
- vector under DOS4Gw, but it does not seem to be working for the INT0
- vector. Also I would like to know why signal() does not work on all
- the compilers as it is part of the ANSI C library. Is there a general
- fault in the implementation of signal for SIG_FPE on most compilers
- foe MS_DOS?
-
-
- | Signal | Interrupt
- Borland C++ v3.1 | Works | Works
- MS-Vis C++ v1.5 | crashes with rutime error | Works in Large Model
- WatcomC v1.0a | See Below | See Below
- DJGPP 1.1.1m5 | crashes | needs porting
-
- Notes on Watcom C:
- With -d2 and -d3 debugging both programs crash with
- a division by 0 runtime error.
- With -d0 and -d1 the program does not crash by the
- DIV0 handler does not get called and the program is not
- reset.
-
-
- /* Test Program 1 */
- /**********************************************************/
- /*** ***/
- /*** TITLE : cc.c ***/
- /*** ***/
- /*** PURPOSE : To test the divide by zero handler. ***/
- /*** SIGNAL based***/
- /*** ***/
- /**********************************************************/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <setjmp.h>
- #include <conio.h>
- #include <float.h>
-
- void RestartProgram();
- void sigDivideByZero(int sig);
- #ifdef __GNUC__
- itoa(int number, char *buffer, int radix);
- #define _fpreset()
- #endif
-
- static sig_atomic_t DIVZ_sig_count;
- static char count_buffer[80];
- jmp_buf env;
-
- int main()
- {
- int f;
- int key;
-
- int dbg_x, dbg_i = 1, dbg_j = 0, dbg_k = 0;
- void (*sigDivideByZero_Orig)(int);
-
-
- sigDivideByZero_Orig = signal(SIGFPE, sigDivideByZero);
- if(sigDivideByZero_Orig == SIG_ERR) {
- puts("");
- puts("Could not establish a new DIV0 handler");
- puts("");
- return 1;
- }
-
-
- if (setjmp(env) == -1) {
- itoa(DIVZ_sig_count, count_buffer, 10);
- puts(count_buffer);
- puts("The DIV by ZERO interupt has been called the above times");
- if(signal(SIGFPE, sigDivideByZero) == SIG_ERR) {
- puts("");
- puts("Could not establish a new DIV0 handler");
- puts("");
- return 1;
- }
-
- }
-
- while(1)
- {
- if(kbhit()) {
- key = getch();
- switch (key) {
- case '0':
- puts("There should be a divide by zero here");
- dbg_x = dbg_i / dbg_j;
- dbg_x = dbg_j / dbg_k;
- break;
- case 'x':
- goto exit;
- case 'X':
- goto exit;
- case 'q':
- goto exit;
- case 'Q':
- goto exit;
- }
- }
- }
-
- exit:
- /* signal(SIGFPE, SIG_DFL); */
- if (signal(SIGFPE, sigDivideByZero_Orig) == SIG_ERR) {
- puts("");
- puts("Could not re-establish the original DIV0 handler");
- puts("");
- return 2;
- }
- }
-
-
- /**********************************************************
- NAME : sigDivideByZero()
- RETURNS :
- PURPOSE : Trap any divide by zero errors which may occur
- COMMENTS :
- **********************************************************/
-
- void sigDivideByZero(int sig)
- {
- DIVZ_sig_count++; /*counts how many times this handler is called*/
-
- puts("");
- puts("Hello from the divide by zero signal handler");
- puts("");
-
- _fpreset();
- RestartProgram();
- }
-
- /**********************************************************
- NAME : RestartProgram()
- RETURNS :
- PURPOSE : Jump to the program restart point
- COMMENTS : Used after motor errors, etc.
- **********************************************************/
-
- void RestartProgram()
- {
- longjmp(env, -1);
- }
-
- #ifdef __GNUC__
- itoa(int number, char *buffer, int radix)
- {
- switch(radix) {
- case 10:
- sprintf(buffer, "%d", number);
- default :
- printf("\n\nERROR IN ITOA RADIX EXITING\n\n");
- }
- }
- #endif
-
- /* Test program 1 ends */
-
-
- ///////////////////////////////////////////////////////////////////////////
-
- /* Test Program 2 */
- /**********************************************************/
- /*** ***/
- /*** TITLE : dd.c ***/
- /*** ***/
- /*** PURPOSE : To test the divide by zero handler. ***/
- /*** INT0 based***/
- /*** ***/
- /**********************************************************/
-
- #define DEBUG_PJ
- #define NORM_SETJMP
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <dos.h>
- #include <process.h>
- #include <string.h>
- #include <conio.h>
- #include <setjmp.h>
- #include "dbg_puts.h"
- #ifdef __GNUC__
- #include <go32.h>
- #include <dpmi.h>
- #define _disable() disable()
- #define _enable() enable()
- #endif
-
- static int dbg_curr_row = 1; /*Start of the screen*/
- static char count_buffer[80];
- static int DIVZ_int_count;
- static unsigned char ports;
- void (__interrupt __far *ori_DIVZ_Handler)();
-
-
-
- /**********************************************************
- NAME : main(int, char *)
- RETURNS :
- PURPOSE :
- COMMENTS :
- **********************************************************/
- void main()
- {
- int f;
- int key;
-
- int dbg_x, dbg_i = 1, dbg_j = 0, dbg_k = 0;
-
- dbg_cls();
- dbg_puts("This is a test program for the divide by zero");
- dbg_puts("handling");
-
- ori_DIVZ_Handler = _dos_getvect(0x0);
-
- _dos_setvect(0, DivideByZero);
-
- if (setjmp(env) == -1) {
- dbg_cls();
- dbg_puts(BLANK_LINE);
- dbg_puts("We have returned from an user handled interupt");
- itoa(DIVZ_int_count, count_buffer, 10);
- dbg_puts(count_buffer);
- dbg_puts("The DIV by ZERO interupt has been called the above times");
- dbg_puts(BLANK_LINE);
- }
-
-
- while(1)
- {
- if(kbhit()) {
- key = getch();
- switch (key) {
- case '0':
- puts("There should be a divide by zero here");
- dbg_x = dbg_i / dbg_j;
- dbg_x = dbg_j / dbg_k;
- break;
- case 'x':
- goto exit;
- case 'X':
- goto exit;
- case 'q':
- goto exit;
- case 'Q':
- goto exit;
- }
- }
- }
-
- exit:
- /*Restore Original interrupt vectors*/
- _dos_setvect(0x0, ori_DIVZ_Handler);
- }
-
- /**********************************************************
- NAME : DivideByZero()
- RETURNS :
- PURPOSE : Trap any divide by zero errors which may occur
- COMMENTS :
- **********************************************************/
-
- void __interrupt __far DivideByZero()
- {
- /* SystemError(s_divide_zero, -1, TRUE);*/
-
- _disable(); /* a critical section */
-
- DIVZ_int_count++; /*counts how many times this handler is called*/
-
- /*
- To directly signal the 8259 programmable interrupt controller
- that this interrupt is finished.
-
- Was not necessary in MSVC, and does not seem to help under
- DOS4GW as the interrupt does not seem to get called.
- */
- #if 0
- ports = (unsigned char) inp(0x0021); /* get IRQ mask status */
- ports |= 0x01; /* unmask for IRQ 0 */
- outp(0x0021, ports); /* write back into OCW */
-
- outp(0x0020, 0x20); /* Signal a non-specific EOI */
- #endif
-
-
- dbg_puts(BLANK_LINE);
- dbg_puts("Hello from the divide by zero interupt handler");
- dbg_puts(BLANK_LINE);
-
- /* ori_DIVZ_Handler();*/
-
- _enable();
-
- RestartProgram();
- }
-
- /**********************************************************
- NAME : RestartProgram()
- RETURNS :
- PURPOSE : Jump to the program restart point
- COMMENTS : Used after motor errors, etc.
- **********************************************************/
-
- void RestartProgram()
- {
- longjmp(env, -1);
- }
-
-
- /***********************************************************************/
- #ifdef DEBUG_PJ
- /* These functions directly output dbg info to the colour text video
- memory.*/
- /* It assumes that we are working in 80x25 mode*/
- /* It allows text output even in interrupt functions */
- /***********************************************************************/
-
- /**********************************************************
- NAME : dbg_puts(char *s_to_print)
- RETURNS :
- PURPOSE : puts a string directly to video memory
- COMMENTS :
- **********************************************************/
- int dbg_puts(char *s_to_print)
- {
- dbg_curr_row++;
- if (dbg_curr_row > 25)
- dbg_curr_row = 1;
- return dbg_puts_pos(s_to_print, dbg_curr_row, DBG_START_COL);
- }
-
- /**********************************************************
- NAME : dbg_puts_ID(int ID)
- RETURNS :
- PURPOSE : puts a string directly to video memory
- COMMENTS :
- **********************************************************/
- void dbg_puts_ID(int ID)
- {
- char *s_error;
- if (dbg_curr_row > 25)
- dbg_curr_row = 1;
-
- dbg_puts(BLANK_LINE);
-
- switch(ID) {
- case s_divide_zero: s_error = "Divide by zero error!!"; break;
- default: s_error = "Unknown error error!!"; break;
- }
-
- dbg_puts_pos(s_error, dbg_curr_row, DBG_START_COL);
-
- dbg_puts(BLANK_LINE);
- }
-
-
- /**********************************************************
- NAME : dbg_puts_pos(char *s_to_print, int row, int col)
- RETURNS :
- PURPOSE : puts a string directly to video memory
- COMMENTS : at a specified location
- **********************************************************/
- int dbg_puts_pos(char *s_to_print, int row, int col)
- {
- int num_of_chars_printed = 0;
-
- #if __WATCOMC__
- char *VidMem = (char *) 0xB8000;
- /*The colour text absolute memory address*/
- #else
- char far *VidMem = (char *) MK_FP(0xB800, 0x0);
- /*The colour text absolute memory address*/
- #endif
- /*To set the video memory to the start of the row and col we want to
- use*/
- row--;
- col--; /*Make row and col zero based*/
- VidMem += (row * 80 * 2);
- VidMem += (col * 2);
- while (*s_to_print) { /*zero terminates*/
- *(VidMem++) = *(s_to_print++);
- VidMem++; /*twice because vid mem is char, attribute pairs*/
- num_of_chars_printed++;
- }
-
- return num_of_chars_printed;
- }
-
- /**********************************************************
- NAME : dbg_cls(void)
- RETURNS : void
- PURPOSE : clears the video memory directly
- COMMENTS :
- **********************************************************/
- void dbg_cls(void)
- {
- #if __WATCOMC__
- char *VidMem = (char *) 0xB8000;
- /*The colour text absolute memory address*/
- #else
- char far *VidMem = (char *) MK_FP(0xB800, 0x0);
- /*The colour text absolute memory address*/
- #endif
- int posVidMem;
-
- dbg_curr_row = 1;
- for (posVidMem = 0; posVidMem < (80 * 25); posVidMem++) {
- *(VidMem++) = 0;
- VidMem++; /* jump over the attributes */
- }
- }
-
- /**********************************************************
- NAME : dbg_beep(void)
- RETURNS : void
- PURPOSE : makes a sonic beep
- COMMENTS :
- **********************************************************/
- void dbg_beep(void)
- {
- puts("\a\a\a");
- }
-
- #endif /*DEBUG_PJ*/
-
-
- #ifdef __GNUC__
- itoa(int number, char *buffer, int radix)
- {
- switch(radix) {
- case 10:
- sprintf(buffer, "%d", number);
- default :
- printf("\n\nERROR IN ITOA RADIX EXITING\n\n");
- }
- }
- #endif
-
- /* end of program 2 */
-
- //////////////////////////////////////////////////////////////////////////
-
- /* the dbg_puts.h header */
-
- /* name dbg_puts.h */
- #ifdef __GNUC__
- #define __far
- #define _far
- #define far
- #define __interrupt
- #endif
-
- #ifndef FALSE
- #define FALSE 0
- #endif
- #ifndef TRUE
- #define TRUE 1
- #endif
-
- #if defined(__WATCOMC__) && !defined(NORM_SETJMP)
- pj_jmp_buf env;
- #else
- jmp_buf env;
- #endif
-
-
- #ifdef DEBUG_PJ
- #define DBG_START_COL 1
- #define BLANK_LINE "
- "
-
- void dbg_puts_ID(int ID);
- int dbg_puts_pos(char *s_to_print, int row, int col);
- int dbg_puts(char *s_to_print);
- void dbg_cls(void);
- void dbg_beep(void);
- #endif
-
- void __interrupt __far DivideByZero();
- void __interrupt __far COM1_Handler();
- int SystemError(int wID, int n, int Error);
- void RestartProgram();
- void main();
- void sigDivideByZero(int sig);
-
-
- #define s_divide_zero 10
- /* the dbg_puts.h header ends*/
-
-
- ////////////////////////////////////////////////////////////////////////////
-
-